/*
 * @Author: 0x9DEFA478
 * @Date: 2025-09-20 22:19:01
 * @LastEditTime: 2025-10-03 23:31:58
 * @LastEditors: 0x9DEFA478
 * @Description: 
 * 
 */
#include "./C_Process.hpp"
#if defined(_WIN32)
#include <windows.h>
#include <sstream>
#include "../Utility/H_String.hpp"
#include "../SystemAPI/Windows/Encoding.hpp"
#include "../SystemAPI/Windows/Error.hpp"
#include "./C_LocalUTF8.hpp"
#else
#endif




#if defined(_WIN32)

static std::string ArgAutoAddEscape(const std::string& Arg){
  std::stringstream ss;
  bool IsExistSpace=false;

  if(Arg.length()<1){
    return "\\\"\\\"";
  }

  for(auto Item:Arg){
    if(Item=='\"'){
      ss<<'\\';
    }else if(Item==' '){
      IsExistSpace=true;
    }
    ss<<Item;
  }

  if(IsExistSpace){
    return '\"'+ss.str()+'\"';
  }

  return ss.str();
}

static H::Pointer<char> CommandToPlatformFormat(const H::Pointer<std::string>::Const& Command){
  std::stringstream ss;

  for(H::usize i=0,length=Command.Length();i<length;i++){
    if(i>0){
      ss<<" ";
    }
    ss<<SystemAPI::Windows::TextEncodeConvert(ArgAutoAddEscape(Command[i]),CP_UTF8,CP_ACP);
  }

  auto argstr=ss.str();

  auto arg=H::Pointer<char>::Array(argstr.length()+1);
  for(std::size_t i=0,length=argstr.length();i<length;i++){
    arg[i]=argstr[i];
  }
  arg[argstr.length()]='\0';

  return arg;
}

namespace{

  class Windows_Pipe{

    public:
      Windows_Pipe(DWORD nSize=0);
      ~Windows_Pipe()noexcept;

      HANDLE Pipe_Read()const noexcept{return pipe_Read;}
      HANDLE Pipe_Write()const noexcept{return pipe_Write;}

      void CloseRead()noexcept;
      void CloseWrite()noexcept;

    private:
      HANDLE pipe_Read;
      HANDLE pipe_Write;
  };

  Windows_Pipe::Windows_Pipe(DWORD nSize){
    SECURITY_ATTRIBUTES sa;
  
    sa.nLength=sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle=TRUE;
    sa.lpSecurityDescriptor=nullptr;
    if(!CreatePipe(&pipe_Read,&pipe_Write,&sa,nSize)){
      throw std::runtime_error("Windows匿名管道创建失败: "+SystemAPI::Windows::GetErrorString());
    }
  }

  Windows_Pipe::~Windows_Pipe()noexcept{
    CloseRead();
    CloseWrite();
  }

  void Windows_Pipe::CloseRead()noexcept{
    if(pipe_Read==nullptr){
      return;
    }
    CloseHandle(pipe_Read);
    pipe_Read=nullptr;
  }
  void Windows_Pipe::CloseWrite()noexcept{
    if(pipe_Write==nullptr){
      return;
    }
    CloseHandle(pipe_Write);
    pipe_Write=nullptr;
  }

  struct ProcessID_Windows{
    DWORD dwProcessId;

    ProcessID_Windows(DWORD dwProcessId)noexcept:dwProcessId(dwProcessId){}
    ~ProcessID_Windows()noexcept=default;

    ProcessID_Windows(const ProcessID_Windows&)noexcept=default;

    bool Valid()const noexcept{return true;}

    H::usize Number()const noexcept{return dwProcessId;}
    bool ValidNumber()const noexcept{return true;}

    std::string String()const noexcept{return std::to_string(dwProcessId);}
    bool ValidString()const noexcept{return true;}
  };

  struct Process_Windows{
    Windows_Pipe output;
    Windows_Pipe input;
    bool usePipeIO;
    bool isStop;
    bool outputIsStop;
    bool inputIsStop;
    int result;
    PROCESS_INFORMATION proc_info;

    H::String::FIFO<char> outputFIFO;

    Process_Windows(const H::Pointer<std::string>::Const& Command,bool pipeIO);
    ~Process_Windows()noexcept;
    
    int Read(char* Buffer,int BufferSize)noexcept;
    bool Write(const void* Buffer,int Length)noexcept;
    bool IsStop()noexcept;
    void WaitStop()noexcept;
    void Stop()noexcept;
    ProcessID_Windows* ID()const noexcept{return new ProcessID_Windows(proc_info.dwProcessId);}

  };

  Process_Windows::Process_Windows(const H::Pointer<std::string>::Const& Command,bool UsePipeIO)
    :output(0),input(0),usePipeIO(UsePipeIO)
    ,isStop(false),outputIsStop(false),inputIsStop(false)
    ,result(-1)
    ,proc_info{0}
  {
    STARTUPINFOA StartupInfo={0};

    auto cmdstr=CommandToPlatformFormat(Command);

    if(!SetHandleInformation(output.Pipe_Read(),HANDLE_FLAG_INHERIT,0)){
      throw std::runtime_error("Windows输出管道设置继承属性失败: "+SystemAPI::Windows::GetErrorString());
    }

    if(!SetHandleInformation(input.Pipe_Write(),HANDLE_FLAG_INHERIT,0)){
      throw std::runtime_error("Windows输入管道设置继承属性失败: "+SystemAPI::Windows::GetErrorString());
    }
    
    StartupInfo.cb=sizeof(STARTUPINFOA);
    if(UsePipeIO){
      StartupInfo.hStdError=output.Pipe_Write();
      StartupInfo.hStdOutput=output.Pipe_Write();
      StartupInfo.hStdInput=input.Pipe_Read();
      StartupInfo.dwFlags=STARTF_USESTDHANDLES;
    }

    if(!CreateProcessA(nullptr,cmdstr.GetPointer(),nullptr,nullptr,TRUE,UsePipeIO?0:CREATE_NEW_PROCESS_GROUP,nullptr,nullptr,&StartupInfo,&proc_info)){
      throw std::runtime_error("Windows进程创建失败 目标命令: "+ SystemAPI::Windows::TextEncodeConvert(cmdstr.GetPointer(),CP_ACP,CP_UTF8) +" 错误: "+SystemAPI::Windows::GetErrorString());
    }

    output.CloseWrite();
    input.CloseRead();
  }

  Process_Windows::~Process_Windows()noexcept{
    if(!IsStop()){
      TerminateProcess(proc_info.hProcess,0);
    }

    CloseHandle(proc_info.hProcess);
    CloseHandle(proc_info.hThread);
  }

  int Process_Windows::Read(char* Buffer,int BufferSize)noexcept{
    DWORD readLength;

    if(outputIsStop||(!usePipeIO)){
      return 0;
    }

    auto res=ReadFile(output.Pipe_Read(),Buffer,BufferSize,&readLength,nullptr);
    if((!res)&&(readLength==0)){
      outputIsStop=true;
      return 0;
    }

    return readLength;
  }

  bool Process_Windows::Write(const void* Buffer,int Length)noexcept{
    DWORD writeLength;

    if(inputIsStop||(!usePipeIO)){
      return false;
    }

    auto res=WriteFile(input.Pipe_Write(),Buffer,Length,&writeLength,nullptr);
    if(!res){
      inputIsStop=true;
      return false;
    }

    return true;
  }

  bool Process_Windows::IsStop()noexcept{
    if(isStop){
      return true;
    }

    if(WAIT_OBJECT_0==WaitForSingleObject(proc_info.hProcess,0)){
      DWORD ExitCode;
      isStop=true;
      GetExitCodeProcess(proc_info.hProcess,&ExitCode);
      result=ExitCode;
      return true;
    }

    return false;
  }

  void Process_Windows::WaitStop()noexcept{
    DWORD ExitCode;
    if(isStop){
      return;
    }
    WaitForSingleObject(proc_info.hProcess,INFINITE);
    isStop=true;
    GetExitCodeProcess(proc_info.hProcess,&ExitCode);
    result=ExitCode;
  }

  void Process_Windows::Stop()noexcept{
    if(isStop){
      return;
    }
    TerminateProcess(proc_info.hProcess,STATUS_TIMEOUT);
    isStop=true;
    result=STATUS_TIMEOUT;
  }

}


using Process_PrivateData=Process_Windows;
using ProcessID_PrivateData=ProcessID_Windows;

#elif defined(__linux__)

#endif


#if defined(_WIN32)||defined(__linux__)
namespace H{

  Process::ID::ID(void* privateData):privateData(privateData){}
  
  Process::ID::ID()noexcept:privateData(nullptr){}

  Process::ID::~ID()noexcept{
    if(privateData!=nullptr){
      delete static_cast<ProcessID_PrivateData*>(privateData);
    }
  }

  Process::ID::ID(const ID& id)
    :privateData(static_cast<void*>(new ProcessID_PrivateData(*static_cast<ProcessID_PrivateData*>(id.privateData))))
  {}

  Process::ID::ID(ID&& id)noexcept:privateData(id.privateData){
    id.privateData=nullptr;
  }

  Process::ID& Process::ID::operator=(const ID& id){
    if(privateData!=nullptr){
      delete static_cast<ProcessID_PrivateData*>(privateData);
    }
    privateData=static_cast<void*>(new ProcessID_PrivateData(*static_cast<ProcessID_PrivateData*>(id.privateData)));
    return *this;
  }

  Process::ID& Process::ID::operator=(ID&& id)noexcept{
    if(privateData!=nullptr){
      delete static_cast<ProcessID_PrivateData*>(privateData);
    }
    privateData=id.privateData;
    id.privateData=nullptr;
    return *this;
  }


  bool Process::ID::Valid()const noexcept{
    return static_cast<ProcessID_PrivateData*>(privateData)->Valid();
  }

  usize Process::ID::Number()const noexcept{
    return static_cast<ProcessID_PrivateData*>(privateData)->Number();
  }

  bool Process::ID::ValidNumber()const noexcept{
    return static_cast<ProcessID_PrivateData*>(privateData)->ValidNumber();
  }

  std::string Process::ID::String()const noexcept{
    return static_cast<ProcessID_PrivateData*>(privateData)->String();
  }

  bool Process::ID::ValidString()const noexcept{
    return static_cast<ProcessID_PrivateData*>(privateData)->ValidString();
  }


  Process::Process()noexcept:privateData(nullptr){}

  Process::Process(const H::Pointer<std::string>::Const& Command,bool UsePipeIO)
    :privateData(static_cast<void*>(new Process_PrivateData(Command,UsePipeIO)))
  {}

  Process::~Process()noexcept{
    if(privateData!=nullptr){
      delete static_cast<Process_PrivateData*>(privateData);
    }
  }


  Process::Process(Process&& process)noexcept:privateData(process.privateData){
    process.privateData=nullptr;
  }

  Process& Process::operator=(Process&& process)noexcept{
    privateData=process.privateData;
    process.privateData=nullptr;
    return *this;
  }

  bool Process::WriteInput(const std::string& str)noexcept{
    auto privateData=static_cast<Process_PrivateData*>(this->privateData);

    return privateData->Write(str.c_str(),str.length());
  }

  H::Pointer<char> Process::ReadOutput()noexcept{
    auto privateData=static_cast<Process_PrivateData*>(this->privateData);
    auto buffer=H::Pointer<char>::Array(128);
    usize readLen;

    if((readLen=privateData->outputFIFO.Length())>0){
      auto r=H::Pointer<char>::Array(readLen);
      privateData->outputFIFO.Read(r.GetPointer(),readLen);
      return r;
    }

    if((readLen=privateData->Read(buffer.GetPointer(),buffer.Length()))>0){
      return buffer.Clone(readLen);
    }

    return H::Pointer<char>();
  }

  H::Pointer<char> Process::ReadLine(char SplitChar)noexcept{
    auto privateData=static_cast<Process_PrivateData*>(this->privateData);
    auto buffer=H::Pointer<char>::Array(128);
    usize readLen;
    H::Pointer<char> s;

    while(!privateData->outputFIFO.ReadLine(s,SplitChar)){
      if((readLen=privateData->Read(buffer.GetPointer(),buffer.Length()))>0){
        privateData->outputFIFO.Write(buffer.GetPointer(),readLen);
      }else{
        return H::Pointer<char>();
      }
    }
    return s;
  }

  bool Process::IsStop()noexcept{
    auto privateData=static_cast<Process_PrivateData*>(this->privateData);
    return privateData->IsStop();
  }

  void Process::WaitStop()noexcept{
    auto privateData=static_cast<Process_PrivateData*>(this->privateData);
    auto buffer=H::Pointer<char>::Array(128);
    usize readLen;

    while((readLen=privateData->Read(buffer.GetPointer(),buffer.Length()))>0){
      privateData->outputFIFO.Write(buffer.GetPointer(),readLen);
    }

    privateData->WaitStop();
  }

  void Process::Stop()noexcept{
    auto privateData=static_cast<Process_PrivateData*>(this->privateData);
    privateData->Stop();
  }

  int Process::Result()noexcept{
    auto privateData=static_cast<Process_PrivateData*>(this->privateData);
    return privateData->result;
  }

  Process::ID Process::GetID()const noexcept{
    auto privateData=static_cast<Process_PrivateData*>(this->privateData);
    return ID(privateData->ID());
  }

}
#endif



